const { createClient } = require('redis');

// 全局Redis客户端实例
let client;

/**
 * 初始化Redis客户端（确保连接成功后再返回）
 */
const redisClient = async () => {
  if (client) return; // 已初始化则直接返回

  try {
    // 1. 先创建客户端实例
    const newClient = createClient({
      url: 'redis://localhost:6379', // 连接地址（根据实际情况修改）
      socket: {
        reconnectStrategy: (retries) => {
          // 自动重连策略（重试5次后放弃）
          if (retries > 5) return null;
          return retries * 1000; // 每次重试间隔递增1秒
        },
      },
    });

    // 2. 监听错误事件
    newClient.on('error', (err) => {
      console.error('Redis客户端错误:', err.message);
    });

    // 3. 等待连接完成后再赋值给全局client
    await newClient.connect();
    client = newClient;
    console.log('Redis连接成功');
  } catch (err) {
    console.error('Redis初始化失败:', err.message);
    throw err; // 抛出错误，避免后续操作使用未初始化的客户端
  }
};

/**
 * 存入数据（支持对象/数组，自动序列化）
 * @param {string} key 键名
 * @param {any} value 要存储的值（支持对象、数组等可序列化类型）
 * @param {number|null} ttl 过期时间（秒，默认不过期）
 */
const setKey = async (key, value, ttl = null) => {
  // 确保客户端已初始化
  if (!client) await redisClient();

  try {
    // 处理Sequelize实例（转为纯对象避免循环引用）
    const serializableValue = value?.toJSON ? value.toJSON() : value;

    // 序列化（处理特殊值）
    const jsonValue = JSON.stringify(serializableValue, (k, v) => {
      // 处理BigInt类型（Redis不支持直接存储BigInt）
      if (typeof v === 'bigint') return v.toString();
      return v;
    });

    // 调用Redis v4+的set方法（需传入选项对象）
    await client.set(key, jsonValue, {});

    // 设置过期时间
    if (ttl !== null && typeof ttl === 'number') {
      await client.expire(key, ttl);
    }

    console.log(`Redis写入成功: key=${key}`);
  } catch (err) {
    console.error(`Redis写入失败 (key=${key}):`, err.message);
    throw err; // 抛出错误供上层处理
  }
};

/**
 * 读取数据（自动反序列化）
 * @param {string} key 键名
 * @returns {any} 解析后的值（对象/数组/基本类型，不存在则返回null）
 */
const getKey = async (key) => {
  if (!client) await redisClient();

  try {
    const value = await client.get(key);
    if (!value) return null;

    // 反序列化（处理BigInt字符串转回BigInt）
    return JSON.parse(value, (k, v) => {
      if (typeof v === 'string' && /^\d+n$/.test(v)) {
        return BigInt(v.slice(0, -1));
      }
      return v;
    });
  } catch (err) {
    console.error(`Redis读取失败 (key=${key}):`, err.message);
    throw err;
  }
};

/**
 * 删除指定键
 * @param {string} key 键名
 */
const delKey = async (key) => {
  if (!client) await redisClient();

  try {
    await client.del(key);
    console.log(`Redis删除成功: key=${key}`);
  } catch (err) {
    console.error(`Redis删除失败 (key=${key}):`, err.message);
    throw err;
  }
};

/**
 * 获取匹配模式的所有键名
 * @param {string} key 键名
 */
const getKeysByPattern = async (pattern) => {
  if (!client) await redisClient();

  try {
    return await client.keys(pattern);
  } catch (err) {
    console.error(`Redis删除失败 (key=${key}):`, err.message);
    throw err;
  }
};

/**
 * 清空所有缓存
 * @param {string} key 键名
 */
const flushAll = async (pattern) => {
  if (!client) await redisClient();
  try {
    await client.flushAll();
    console.error(`Redis缓存清理成功`);
  } catch (err) {
    console.error(`Redis缓存清理失败:`, err.message);
    throw err;
  }
};

/**
 * 关闭Redis连接（程序退出时调用）
 */
const closeRedis = async () => {
  if (client) {
    await client.disconnect();
    client = null;
    console.log('Redis连接已关闭');
  }
};

module.exports = {
  redisClient,
  setKey,
  getKey,
  delKey,
  closeRedis,
  getKeysByPattern,
};
